package com.sciology.xpathutility.view;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;

import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import net.sf.saxon.xpath.StandaloneContext;
import net.sf.saxon.xpath.XPathEvaluator;
import net.sf.saxon.xpath.XPathFactoryImpl;

import org.w3c.dom.Attr;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.sciology.xpathutility.XPathWorkbookActivator;
import com.sciology.xpathutility.utils.XMLNamespaceContext;
import com.sciology.xpathutility.utils.NamespaceContextImpl;
import com.sciology.xpathutility.utils.XPathUtilLog;

public class XMLUtilities {

	private static XPathFactory xpf = null;
	private static XPath xp = null;

    public static NodeList evaluateXPathDOM(Reader xmlSource, String xpathExpr) throws Exception {
        DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
        dfactory.setNamespaceAware(true);
        DocumentBuilder builder = dfactory.newDocumentBuilder();
        final org.w3c.dom.Document document =  builder.parse(new InputSource(xmlSource));
        Map ns = XMLUtilities.getMyNamespaces(document.getDocumentElement());

        XPathFactory xpf = new XPathFactoryImpl();
        XPathEvaluator xpe = (XPathEvaluator) xpf.newXPath();

        NamespaceContextImpl namespaceContext = new NamespaceContextImpl();
        namespaceContext.addNamespaceMap(ns);

        StandaloneContext stdCtx = new StandaloneContext();
        stdCtx.setNamespaceContext(namespaceContext);
        if (namespaceContext.getNamespaceURI("") != null) {
            stdCtx.setDefaultElementNamespace(namespaceContext.getNamespaceURI(""));
        }
        xpe.setStaticContext(stdCtx);

        NamespaceContext nsCtx = new XMLNamespaceContext(ns);
        xpe.setNamespaceContext(nsCtx);
        XPathExpression xpexpression = xpe.compile(xpathExpr);
        NodeList result = (NodeList)xpexpression.evaluate(document, XPathConstants.NODESET);
        return result;
    }

	public static NodeList defaultErrorXML(final String message) {
		org.w3c.dom.Document headerDocment = createDocument();
		Element xpathResult = headerDocment.createElement("Result");
		Element executionResult = headerDocment.createElement("ExecutionResult");
		Text defaultMessage = headerDocment.createTextNode("XPath did not yield any result.");
		xpathResult.appendChild(executionResult);
		executionResult.appendChild(defaultMessage);
		headerDocment.appendChild(xpathResult);
		Element resultMessage = headerDocment.createElement("ErrorMessage");
		Text errorMessage = headerDocment.createTextNode(message);
		resultMessage.appendChild(errorMessage);
		xpathResult.appendChild(resultMessage);
		return headerDocment.getChildNodes();
	}
	

	public static org.w3c.dom.Document convertTextoXML(String text) {
		org.w3c.dom.Document d = null;
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			StringReader stringReader=  new StringReader(text);
			System.out.println("Printing test:"+text);
			d = factory.newDocumentBuilder().parse(new InputSource(stringReader));
		} catch (ParserConfigurationException e) {
			XPathUtilLog.logError(XPathWorkbookActivator.getDefault(), e.toString());
		} catch (SAXException e) {
			XPathUtilLog.logError(XPathWorkbookActivator.getDefault(), e.toString());
		} catch (IOException e) {
			XPathUtilLog.logError(XPathWorkbookActivator.getDefault(), e.toString());
		}
		return d;
	}
	

	public static String convertXMLtoString(org.w3c.dom.Document xmlDoc) {
		StreamResult sr ;
		java.io.StringWriter sw = null;
		try {
			// Prepare the DOM document for writing
			Source source = new DOMSource(xmlDoc);
			// Prepare the output file
			// Write the DOM document to the file
			Transformer xformer = TransformerFactory.newInstance().newTransformer();
			xformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount","4");
			xformer.setOutputProperty(OutputKeys.INDENT, "yes");
			xformer.setOutputProperty(OutputKeys.METHOD,"html");
			sw = new java.io.StringWriter();
			sr= new StreamResult(sw);
			xformer.transform(source, sr);
		} catch (TransformerConfigurationException e) {
			XPathUtilLog.logError(XPathWorkbookActivator.getDefault(), e.toString());
        } catch (TransformerException e) {
			XPathUtilLog.logError(XPathWorkbookActivator.getDefault(), e.toString());
        }
		return sw.toString();

	}

	
	public static DocumentFragment parseXml(org.w3c.dom.Document doc, String fragment) {
	 	DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
	 	factory.setIgnoringComments(true);
	 	org.w3c.dom.Document d = null;
		try {
			StringReader stringReader = new StringReader(fragment);
			d = factory.newDocumentBuilder().parse(new InputSource(stringReader));
		} catch (SAXException e) {
			XPathUtilLog.logError(XPathWorkbookActivator.getDefault(), e.toString());
		} catch (IOException e) {
			XPathUtilLog.logError(XPathWorkbookActivator.getDefault(), e.toString());
		} catch (ParserConfigurationException e) {
			XPathUtilLog.logError(XPathWorkbookActivator.getDefault(), e.toString());
		}
	 	Node node = doc.importNode(d.getDocumentElement(), true);
	 	DocumentFragment docfrag = doc.createDocumentFragment();
	 	  while (node.hasChildNodes()) {
                docfrag.appendChild(node.removeChild(node.getFirstChild()));
            }
        return docfrag;
 }

	public static org.w3c.dom.Document createDocument(){
		org.w3c.dom.Document d = null;
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			d = factory.newDocumentBuilder().newDocument();
		} catch (ParserConfigurationException e) {
			XPathUtilLog.logError(XPathWorkbookActivator.getDefault(), e.toString());
		}
		return d;
	}
	
    public static Map getMyNamespaces(Element el) {
        HashMap mine = new HashMap();
        NamedNodeMap nnm = el.getAttributes();
        int len = nnm.getLength();
        for (int i=0; i < len; ++i) {
            Attr a = (Attr) nnm.item(i);
            if (isNSAttribute(a)) {
            	mine.put(getNSPrefixFromNSAttr(a),a.getValue());
            }
        }
        return mine;
    }

    private static String getNSPrefixFromNSAttr(Attr a) {
        assert a != null;
        if (a.getPrefix() == null) {
            return "";
        }
        return a.getName().substring(a.getPrefix().length()+1);
    }

    private static boolean isNSAttribute(Attr a) {
        return a.getName().startsWith("xmlns");
    }

	// XPath utility to know if an expression is valid or not.
	public static boolean isValidXPathExpression(final String xPathExpression) throws Exception {
		boolean isValid = true;
		try {
            xpf = XPathFactory.newInstance(XPathConstants.DOM_OBJECT_MODEL);
            xp = xpf.newXPath();
	        XPathExpression xpe = xp.compile(xPathExpression);
		}
		catch (XPathExpressionException ex) {
			isValid = false;
			throw new Exception((( Throwable )ex.getCause()).getMessage());
		}
		return isValid;
	}

}
